package blackstar.performance.logger;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;

import blackstar.performance.LogStat;

/**
 * @author RayWoo (ayufox@gmail.com)
 * 
 */
public final class BufferWriter
{
	private final static Logger LOGGER = Logger.getLogger(BufferWriter.class
			.getName());

	private char[] backBuffer;
	private char[] buffer;
	private int length = 0;
	private final int bufferLength;

	private final ExecutorService executorService;
	private FileWriter fileWriter;
	private LogStat logStat;

	public BufferWriter(int bufferLength, ExecutorService executorService)
	{
		this.bufferLength = bufferLength;
		this.buffer = new char[this.bufferLength];
		this.backBuffer = new char[this.bufferLength];
		this.executorService = executorService;
	}
	
	public void setLogStat(LogStat logStat)
	{
		this.logStat = logStat;
	}

	/**
	 * 日志文件切换
	 * 
	 * @param file
	 * @throws IOException
	 */
	public synchronized void resetFile(File file) throws IOException
	{
		if (this.fileWriter != null)
		{
			flush();
			this.fileWriter.close();
			this.fileWriter = null;
		}

		try
		{
			this.fileWriter = new FileWriter(file, true);
		} catch (FileNotFoundException ex)
		{
			String parentName = file.getParent();
			if (parentName != null)
			{
				File parentDir = new File(parentName);
				if (!parentDir.exists() && parentDir.mkdirs())
				{
					fileWriter = new FileWriter(file, true);
				} else
				{
					throw ex;
				}
			} else
			{
				throw ex;
			}
		}
	}

	public synchronized void close() throws IOException
	{
		if (this.fileWriter != null)
		{
			LOGGER.info("flush performance data immediately to file:" + length);
			if (length > 0)
			{
				try
				{
					fileWriter.write(this.buffer, 0, length);
					fileWriter.flush();
				} catch (Exception e)
				{
					LOGGER.warning("Error When Write:" + e.getMessage());
				}
				
				this.buffer = null;
				this.length = 0;
			}

			this.fileWriter.close();
			this.fileWriter = null;
		}
	}

	public synchronized void write(StringBuilder message)
	{
		int byteLength = message.length();
		// 已满
		if (byteLength > (bufferLength - length))
		{
			flush();
		}

		// 大块的数据,直接写
		if (byteLength > bufferLength)
		{
			char[] writeBuffer = new char[byteLength];
			message.getChars(0, byteLength, writeBuffer, 0);
			writeToFile(writeBuffer, byteLength);
		} else
		{
			message.getChars(0, byteLength, this.buffer, this.length);
			this.length += byteLength;
		}
	}

	public synchronized void flush()
	{
		if (length > 0)
		{
			//swap
			final char[] tmpBuffer = this.buffer;
			this.buffer = this.backBuffer;
			this.backBuffer = tmpBuffer;
			
			final int writeLength = length;	
			this.length = 0;
			
			//write
			writeToFile(this.backBuffer, writeLength);
		}
	}

	private void writeToFile(final char[] oldBuffer, final int writeLength)
	{
		this.executorService.execute(new Runnable()
		{
			public void run()
			{
				try
				{
					long begin = System.nanoTime();
					fileWriter.write(oldBuffer, 0, writeLength);
					fileWriter.flush();
					if (logStat != null)
					{
						logStat.log(writeLength, (System.nanoTime() - begin));
					}
				} catch (Exception e)
				{
					LOGGER.warning("Error When Write:" + e.getMessage());
				}
			}
		});
	}
}